//package game;
package processing.test.mechaapp;

import processing.core.*;

//import processing.data.*; 


import java.util.Random;
import java.util.ArrayList;
import java.util.Iterator;

import android.content.Context;
import android.os.Looper;
import android.os.Vibrator;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class MechaApp extends PApplet {
	// 2D Array of objects
	Atom grid[][];
	

	// Number of columns and rows in the grid
	private final int cols = 8;
	private int rows = 8;
	
	int Ox = 5;
	int Ca = 5;
	int Ni = 5;
	int Hy = 35;
	int Bk = 50;
	
	float sz;
	Random rand = new Random();
	Atom selectedAtom = null;
	
	//these vars are for spawning new atoms with respect to the number of moves a player
	//makes
	int spawnAtom;
	int numOfMoves;
	int numAtoms = 0;
	
	private ArrayList<Molecule> history;
	
	//ANDROID gestures
	GestureDetector listener;
	GestureListener context;
	boolean gestureRunning;
	
	//ANDROID vibrate
	Vibrator vibrate;
	
	public void setup() {
		orientation(PORTRAIT); // for ANDRIOD
	  //size(400,400);
	  smooth();
	  Atom.setApplet(this); //This statement needs to be called in order to use proccessing's API!
	  sz = (width/cols);
	  Atom.size = (float) (sz/(1.4));
	  grid = new Atom[cols][rows];
	  for (int i = 0; i < cols; i++) {
	    for (int j = 0; j < rows; j++) {
	    	spawnNewAtom(i,j,Spawn(Bk,Hy,Ox,Ni,Ca));
	    }
	  }
	  
	  spawnAtom = 10;//this can be changed with respect to difficulty
	  numOfMoves = 0;
	  
	  history = new ArrayList<Molecule>();
	  
	  //ANDROID gestures
	  Looper.prepare();
	  context = new GestureListener(this);
	  context.setSingleTap("singleTap");
	  context.setSwipe("swipe");
	  listener = new GestureDetector(context);
	  gestureRunning = true;
	  
	  //ANDROID vibrate
	  vibrate = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);
	  
	  Log.i("SETUP","Setup is completed");
	}
 
	public void draw() {
		  background(0);
		  for (int i = 0; i < cols; i++) {
	        for (int j = 0; j < rows; j++) {
	        	grid[i][j].drawLines();
	        }
		  }
		  
		  for (int i = 0; i < cols; i++) {
		    for (int j = 0; j < rows; j++) {
		    		grid[i][j].drawMe();	    		
		    }
		  }
	}
	
	//JAVA
	/*public void mouseClicked(){		
		Atom temp;
		float tempX;
		float tempY;
		int tempI;
		int tempJ;
		
		for (int i = 0; i < cols; i++) {
		    for (int j = 0; j < rows; j++) {
		    	temp = grid[i][j];

		    	if(atomOver(temp.xChord, temp.yChord, temp.size)){
		    		if(selectedAtom==null){
		    			if(temp instanceof blankAtom) continue;
		    			selectedAtom = temp;
		    			selectedAtom.highlight = true;//Highlights the atom
		    			breakAtom(selectedAtom);
		    			return;//these return statments are "sensitive"
		    		}
		    		else{
		    			if(temp != selectedAtom  && nextToMe(selectedAtom.ith, selectedAtom.jth, i,  j)){
		    				breakAtom(temp);
		    			
		    				//switches in the grid array
		    				grid[i][j] = grid[selectedAtom.ith][selectedAtom.jth];
		    				grid[selectedAtom.ith][selectedAtom.jth] = temp;
		    			
		    				//switches graphically here		    			
		    				tempX = selectedAtom.xChord;
		    				tempY = selectedAtom.yChord;
		    				tempI = selectedAtom.ith;
		    				tempJ = selectedAtom.jth;
		    			
		    				selectedAtom.xChord = temp.xChord;
		    				selectedAtom.yChord = temp.yChord;
		    				selectedAtom.ith = temp.ith;
		    				selectedAtom.jth = temp.jth;
		    			
		    				temp.xChord = tempX;
		    				temp.yChord = tempY;
		    				temp.ith = tempI;
		    				temp.jth = tempJ;
		    			
		    				setAtom(selectedAtom);		    				
		    			}
		    			setAtom(temp);
		    			selectedAtom.highlight = false;
		    			
		    			processMolecule(selectedAtom.molecule);
		    			if(!(temp instanceof blankAtom))
		    				processMolecule(temp.molecule);
		    			
		    			selectedAtom = null;
		    			
		    			//history debugging
		    			//System.out.println("History ("+history.size()+")\n"+history.toString());
		    			
		    			//spawns a new atom if the number of moves matches the
		    			//number of moves it takes to spawn a new atom. This method may need to be "biased"
		    			numOfMoves++;
		    			if(numOfMoves == spawnAtom){
		    				//don't create a new atom if the board is already full
		    				if(numAtoms < 64){
		    					int ith = rand.nextInt(8);
		    					int jth = rand.nextInt(8);
		    					while(!(grid[ith][jth] instanceof blankAtom)){
		    						ith = rand.nextInt(8);
		    						jth = rand.nextInt(8);
		    					}
		    				
		    					spawnNewAtom(ith,jth,Spawn(0,Hy,Ox,Ni,Ca));
		    				}
		    				numOfMoves = 0;
		    			}
		    			
		    			//Game over condition
		    			if(numAtoms == 0){
		    				System.out.println("============================================");
		    				System.out.println("------------------GAME OVER-----------------");
		    				System.out.println("============================================");
		    				
		    				//TODO SHOW SCORE
		    				//TODO PROMPT NEW GAME SELECTION
		    			}
		    			
		    			return;//these return statments are "sensitive"
		    		}
		    	}
		    }
		}
	}
	*/

	//fired when the screen is first touch, which in turns fires onTouchEvent
	//this is the new mouseClicked() method!
	//------not continuous-----
	//ANDROID
	public boolean surfaceTouchEvent(MotionEvent event){
		mouseX = (int) event.getX();
		mouseY = (int) event.getY();
		return false;
	}
	
	//fired when the user is touching the screen
	//-------is continuous-------
	//ANDROID
	public boolean onTouchEvent(MotionEvent event){
		if(gestureRunning)
			return listener.onTouchEvent(event);
		else
			return false;
	}
	
	//sets the selectedAtom to the atom the user clicked on
	//it DOES NOT set or break bonds
	//-------for game mode only---------
	//ANDROID
	public void singleTap(){
		Atom temp = null;
		boolean found = false;
		
		for (int i = 0; i < cols && !found; i++) {
		    for (int j = 0; j < rows && !found; j++) {
		    	temp = grid[i][j];
		    	
		    	if(!(temp instanceof blankAtom) && atomOver(temp.xChord, temp.yChord, temp.size)){
		    		found = true;
		    	}
		    	
		    }
		}
		
		if(found && selectedAtom != null && selectedAtom != temp){
			selectedAtom.highlight = false;
			selectedAtom = temp;
			selectedAtom.highlight = true;
			
		}else if(found && selectedAtom == null){
			selectedAtom = temp;
			selectedAtom.highlight = true;
		}else if(selectedAtom != null){
			selectedAtom.highlight = false;
			selectedAtom = null;
		}
	}
	
	//only gets called when the gestures are running and when a swipe gesture is made
	//-----for game mode only------
	//ANDROID
	public void swipe(int direction){
		if(selectedAtom == null) return;
		Atom temp = null;
		
		switch(direction){
		case GestureListener.SWIPE_UP: 
			if(selectedAtom.jth > 0)
				temp = grid[selectedAtom.ith][selectedAtom.jth-1];
			break;
		case GestureListener.SWIPE_DOWN: 
			if(selectedAtom.jth < rows-1)
				temp = grid[selectedAtom.ith][selectedAtom.jth+1];
			break;
		case GestureListener.SWIPE_LEFT: 
			if(selectedAtom.ith > 0)
				temp = grid[selectedAtom.ith-1][selectedAtom.jth];
			break;
		case GestureListener.SWIPE_RIGHT: 
			if(selectedAtom.ith < cols-1)
				temp = grid[selectedAtom.ith+1][selectedAtom.jth];
			break;
		
		default: Log.e("SWIPE", "Direction not recognized");
		}
		
		if(temp != null){
			breakAtom(temp);
			breakAtom(selectedAtom);
			
			//switch in the grid
			grid[temp.ith][temp.jth] = grid[selectedAtom.ith][selectedAtom.jth];
			grid[selectedAtom.ith][selectedAtom.jth] = temp;
			
			//switch graphically here
			float tempX = selectedAtom.xChord;
			float tempY = selectedAtom.yChord;
			int tempI = selectedAtom.ith;
			int tempJ = selectedAtom.jth;
			
			selectedAtom.xChord = temp.xChord;
			selectedAtom.yChord = temp.yChord;
			selectedAtom.ith = temp.ith;
			selectedAtom.jth = temp.jth;
		
			temp.xChord = tempX;
			temp.yChord = tempY;
			temp.ith = tempI;
			temp.jth = tempJ;
			
			setAtom(temp);
			setAtom(selectedAtom);

			processMolecule(selectedAtom.molecule);
			if(!(temp instanceof blankAtom))
				processMolecule(temp.molecule);
			
			numOfMoves++;
			if(numOfMoves == spawnAtom){
				//don't create a new atom if the board is already full
				if(numAtoms < 64){
					int ith = rand.nextInt(8);
					int jth = rand.nextInt(8);
					while(!(grid[ith][jth] instanceof blankAtom)){
						ith = rand.nextInt(8);
						jth = rand.nextInt(8);
					}
				
					spawnNewAtom(ith,jth,Spawn(0,Hy,Ox,Ni,Ca));
				}
				numOfMoves = 0;
			}
			
			//Game over condition
			if(numAtoms == 0){
				Log.i("GAME", "--------GAME OVER-------");
				
				//TODO SHOW SCORE
				//TODO PROMPT NEW GAME SELECTION
			}
		}
		
	}
	
	//the order of bonding goes as follows:
	//up, n.east, right, s.east, down, s.west, left, n.west
	private void setAtom(Atom atom){
		if(atom instanceof blankAtom) return;
		
		if(atom.jth > 0)
			atom.makeBond(grid[atom.ith][atom.jth-1]);//up
		if ((atom.ith < cols -1) && (atom.jth > 0))
			atom.makeBond(grid[atom.ith+1][atom.jth-1]);//n.east
		if(atom.ith < cols-1)
			atom.makeBond(grid[atom.ith+1][atom.jth]); //right	
		if ((atom.ith < cols -1) && (atom.jth < rows -1))
	        atom.makeBond(grid[atom.ith+1][atom.jth+1]);//s.east 
		if(atom.jth < rows-1)
			atom.makeBond(grid[atom.ith][atom.jth+1]);//down
		if ((atom.ith > 0) && (atom.jth < rows -1))
			atom.makeBond(grid[atom.ith-1][atom.jth+1]);//s.west
		if(atom.ith > 0)
	        atom.makeBond(grid[atom.ith-1][atom.jth]);//left
		if ((atom.ith > 0) && (atom.jth > 0))
			atom.makeBond(grid[atom.ith-1][atom.jth-1]);//n.west
		
		//TODO Check molecule history and update difficulty settings
	}
	
	private void breakAtom(Atom atom){
		if(atom instanceof blankAtom) return;
				
		if(atom.jth > 0)
			atom.breakBond(grid[atom.ith][atom.jth-1]);//up
		if ((atom.ith < cols -1) && (atom.jth > 0))
			atom.breakBond(grid[atom.ith+1][atom.jth-1]);//n.east
		if(atom.ith < cols-1)
			atom.breakBond(grid[atom.ith+1][atom.jth]); //right	
		if ((atom.ith < cols -1) && (atom.jth < rows -1))
	        atom.breakBond(grid[atom.ith+1][atom.jth+1]);//s.east 
		if(atom.jth < rows-1)
			atom.breakBond(grid[atom.ith][atom.jth+1]);//down
		if ((atom.ith > 0) && (atom.jth < rows -1))
			atom.breakBond(grid[atom.ith-1][atom.jth+1]);//s.west
		if(atom.ith > 0)
	        atom.breakBond(grid[atom.ith-1][atom.jth]);//left
		if ((atom.ith > 0) && (atom.jth > 0))
			atom.breakBond(grid[atom.ith-1][atom.jth-1]);//n.west
	}
	
	//this method determines if the mouse is over an atom
	//returns true if it is, and false otherwise- jt
	private boolean atomOver(float x, float y, float diameter){
		float disX = x-this.mouseX;
		float disY = y-this.mouseY;
		if(sqrt(sq(disX)+sq(disY)) < diameter/2){
			return true;
		}else{
			return false;
		}
		
	}

	//returns a value 0-4 with certain probabilities
	private int Spawn(int B, int H, int O, int N, int C){	
		//changed the array size from 100 because the sum of all
		//the numbers in the parameter may not equal 100 - jt
		int[] Chance = new int [B+H+O+N+C];
		int i,result;
		int j = 0;
		
		for(i = 0; i < B; i++){
			Chance[j]= 0;
			j++;
		}
		for(i = 0; i < H; i++){
			Chance[j]= 1;
			j++;
		}
		for(i = 0; i < O; i++){
			Chance[j]= 2;
			j++;
		}
		for(i = 0; i < N; i++){
			Chance[j]= 3;
			j++;
		}
		for(i = 0; i < C; i++){
			Chance[j]= 4;
			j++;
		}
	
		result = rand.nextInt(B+H+O+N+C);
		
		return Chance[result];
	
	}
	
	//creates a new atom in the grid at indices i and j
	//with the third parameter determining which atom will be created - jt
	private void spawnNewAtom(int i, int j, int determinate){
		numAtoms++;
		switch(determinate){
		case 1: grid[i][j] = new Hydrogen(i*sz+(sz/2), j*sz+(sz/2), i, j); break;
		case 2: grid[i][j] = new Oxygen(i*sz+(sz/2), j*sz+(sz/2), i, j); break;
		case 3: grid[i][j] = new Nitrogen(i*sz+(sz/2), j*sz+(sz/2), i, j); break;
		case 4: grid[i][j] = new Carbon(i*sz+(sz/2), j*sz+(sz/2), i, j); break;
		
		default:
			grid[i][j] = new blankAtom(i*sz+(sz/2), j*sz+(sz/2), i, j); numAtoms--;
		}
	}
	
	//this method will be responsible for erasing molecules, adding molecules to history
	//and give points based on the molecule created.
	private void processMolecule(Molecule molecule){
		if(molecule.checkCompleteness()){
			
			molecule.eraseMolecule(grid,sz);//removes molecule from grid if it is complete.
			numAtoms -= molecule.count;
			
			vibrate.vibrate(500);//vibrate the phone
			Log.i("VIBRATOR", "phone vibrated for 500 milliseconds");
			
			//add molecule to the history list if it does not equal to any of the molecules
			//in the history. that way we dont have "repeat" molecules in the history
			Iterator<Molecule> itr = history.iterator();
			Molecule tempMolecule;
			while(itr.hasNext()){
				tempMolecule = itr.next();
				if(molecule.compareTo(tempMolecule))
					return;
			}
			history.add(molecule);
		}
	}
	
	//checks if the molecule is right next to it
	//JAVA
	/*
	private boolean nextToMe(int x, int y, int x2, int y2){
		if((x+1==x2||x==x2||x-1==x2)&&(y-1==y2||y==y2||y+1==y2)){
			return true;
		}
		return false;
	}*/
}

